package rbac

import (
	"context"
	"gitee.com/bobo-rs/creative-framework/pkg/sredis"
	"gitee.com/bobo-rs/innovideo-services/consts"
	"gitee.com/bobo-rs/innovideo-services/enums"
	"gitee.com/bobo-rs/innovideo-services/framework/dao"
	"gitee.com/bobo-rs/innovideo-services/framework/model"
	"gitee.com/bobo-rs/innovideo-services/framework/model/entity"
	"gitee.com/bobo-rs/innovideo-services/framework/service"
	"gitee.com/bobo-rs/innovideo-services/library/exception"
	"gitee.com/bobo-rs/innovideo-services/library/tools"
	"github.com/gogf/gf/v2/database/gdb"
	"github.com/gogf/gf/v2/frame/g"
	"time"
)

// SaveAdminUser 保存管理员信息
func (r *sRbac) SaveAdminUser(ctx context.Context, user model.UserAdminSaveItem) error {
	// 保存管理员信息
	return dao.UserAdmin.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
		// 保存管理员信息
		res, err := dao.UserAdmin.Ctx(ctx).OmitEmpty().Save(user.UserAdmin)
		if err != nil {
			return exception.New(`保存管理员信息失败`)
		}

		// 管理员ID
		adminId, err := res.LastInsertId()
		if err != nil {
			return exception.New(`管理员保存失败`)
		}

		// 保存部门
		if len(user.DepartId) > 0 {
			err = r.SaveAdminDepartment(ctx, uint(adminId), user.DepartId...)
			if err != nil {
				return err
			}
		}

		// 保存自定义权限
		if len(user.PermId) > 0 {
			err = r.SavePermAssoc(ctx, uint(adminId), enums.PermAssocTypeUser, user.PermId...)
		}
		return err
	})
}

// GetAdminUserList 获取管理员列表
func (r *sRbac) GetAdminUserList(ctx context.Context, in model.UserAdminGetListInput) (out *model.UserAdminGetListOutput, err error) {
	out = &model.UserAdminGetListOutput{}

	// 获取管理员列表
	err = r.UserAdminModel().ListAndTotal(ctx, model.CommonListAndTotalInput{
		Where:                r.UserAdminWhere(in.UserAdminWhereItem),
		CommonPaginationItem: in.CommonPaginationItem,
	}, &out.Rows, &out.Total)
	if err != nil {
		return nil, err
	}

	// 到底了
	if len(out.Rows) == 0 {
		return out, nil
	}

	// 格式化
	for _, item := range out.Rows {
		r.FormatUserAdmin(&item)
	}
	return
}

// GetUserAdminDetail 获取管理员详情
func (r *sRbac) GetUserAdminDetail(ctx context.Context, id uint) (detail *model.UserAdminGetDetailOutput, err error) {
	detail = &model.UserAdminGetDetailOutput{}
	// 获取管理员详情
	if err = r.ProcessAdminDetailByAid(ctx, id, &detail); err != nil {
		return nil, exception.New(`管理员信息不存在`)
	}

	// 获取部门ID
	detail.DepartId, _ = r.GetDepartmentIdByAid(ctx, id)
	if len(detail.DepartId) > 0 {
		detail.DepartList, _ = r.GetDepartNameByDepartId(ctx, detail.DepartId...)
	}

	// 获取权限列表
	detail.PermId, _ = r.GetPermissionsIdByAdminId(ctx, id)
	return
}

// RemoveUserAdmin 移除管理员信息
func (r *sRbac) RemoveUserAdmin(ctx context.Context, id uint) error {
	if id == 0 {
		return exception.New(`缺少管理员ID`)
	}
	// 移除管理员
	return dao.UserAdmin.Transaction(ctx, func(ctx context.Context, tx gdb.TX) error {
		_, err := tx.Ctx(ctx).Delete(dao.UserAdmin.Table(), g.Map{
			dao.UserAdmin.Columns().Id: id,
		})
		if err != nil {
			return exception.New(`移除管理员失败，请重试`)
		}

		// 移除关联部门
		err = r.RemoveAdminDepartByAid(ctx, id)
		if err != nil {
			return err
		}

		// 移除关联权限
		if err = r.RemovePermAssocByAssocId(ctx, enums.PermAssocTypeUser, id); err != nil {
			return err
		}
		return nil
	})
}

// AuthInviteAdminUser 登录用户直接授权绑定用户
func (r *sRbac) AuthInviteAdminUser(ctx context.Context, token string) error {
	// 获取带用户授权
	userAuth := service.BizCtx().GetUser(ctx)
	if userAuth == nil {
		return exception.NewCodef(enums.ErrorNotLogoutIn, ``)
	}

	// 授权注册管理员
	return r.AddInviteAdminUser(ctx, model.UserAdminInviteAddItem{
		Uid:      userAuth.Uid,
		Nickname: userAuth.Nickname,
		Token:    token,
	})
}

// AddInviteAdminUser 添加管理员邀请注册用户
func (r *sRbac) AddInviteAdminUser(ctx context.Context, invite model.UserAdminInviteAddItem) (err error) {
	var (
		cacheKey  = tools.CacheKey(consts.AdminInviteUserLock, invite.Token)
		cacheFunc = func(ctx context.Context) (interface{}, error) {
			// 添加邀请用户
			if err = r.processAddInviteAdminUser(ctx, invite); err != nil {
				return nil, err
			}
			return invite.Uid, nil
		}
	)

	// 实例缓存服务
	cacheService := sredis.NewCache()
	defer func() {
		if err == nil {
			// 释放占用
			_, _ = cacheService.Remove(ctx, cacheKey)
		}
	}()

	// 锁定3秒钟
	res, err := cacheService.GetOrSetFuncLock(
		ctx, cacheKey, cacheFunc, time.Millisecond*3,
	)
	if err != nil {
		return err
	}

	// 是否同一个用户[避免出现被抢占]
	if res.Uint() != invite.Uid {
		return exception.Newf(`当前邀请已被使用，请勿占用`, res.Uint())
	}
	return nil
}

// processAddInviteAdminUser 处理并添加管理员邀请用户数据
func (r *sRbac) processAddInviteAdminUser(ctx context.Context, invite model.UserAdminInviteAddItem) error {
	// 验证用户是否已经注册管理员
	if b := r.CheckAdminExistsByUid(ctx, invite.Uid); b {
		return nil // 已注册
	}

	// 获取邀请权限并验证有效性
	detail, err := r.GetInviteUserByToken(ctx, invite.Token)
	if err != nil {
		return err
	}

	// 保存数据
	err = r.SaveAdminUser(ctx, model.UserAdminSaveItem{
		UserAdmin: entity.UserAdmin{
			UserId:        invite.Uid,
			ManageName:    invite.Nickname,
			IsSuperManage: detail.IsSuperManage,
		},
		DepartId: r.parseInviteDepartId(detail.DepartId),
	})
	if err != nil {
		return err
	}

	// 更新邀请信息
	if err = r.InviteModel().Update(ctx, g.Map{dao.AdminInvite.Columns().Token: invite.Token}, g.Map{
		dao.AdminInvite.Columns().Uid:    invite.Uid,
		dao.AdminInvite.Columns().Status: enums.AdminInviteStatusOne,
	}); err != nil {
		return err
	}

	return nil
}
